/*
 * Copyright 2024 Broadcom Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "auth_mgr_include.h"
#include "auth_mgr_client.h"
#include "auth_mgr_struct.h"
#include "pac_cfg_authmgr.h"

extern authmgrCB_t *authmgrCB;

extern authmgrCnfgrState_t authmgrCnfgrState;

/*********************************************************************
* @purpose  Handles events generated by NIM
*
* @param    intIfNum   @b{(input)) internal interface number
* @param    intfEvent  @b{(input)) interface event
* @param    correlator @b{(input)) Correlator for the event
* @param    eventData  @b((input)) Additional event specific data
*
* @returns   SUCCESS  on a successful operation
* @returns   FAILURE  for any error
*
* @comments
*
* @end
*********************************************************************/
RC_t authmgrIntfChangeCallback (uint32 intIfNum, uint32 intfEvent,
                                   NIM_CORRELATOR_t correlator,
                                   NIM_EVENT_SPECIFIC_DATA_t eventData)
{
  authmgrIntfChangeParms_t parms;

  parms.intfEvent = intfEvent;
  parms.nimCorrelator = correlator;

  return authmgrIssueCmd (authmgrIntfChange, intIfNum, &parms);
}

/*********************************************************************
* @purpose  Handles startup notification from NIM
*
* @param    startupPhase   @b{(input)) startupPhase 
*
* @returns   SUCCESS  on a successful operation
* @returns   FAILURE  for any error
*
* @comments
*
* @end
*********************************************************************/
void authmgrIntfStartupCallback (NIM_STARTUP_PHASE_t startupPhase)
{

  /* hold NIM till authmgr is ready */
  while (!(AUTHMGR_IS_READY))
  {
     LOGF ( LOG_SEVERITY_ERROR,
             "Received an interface startup callback while AUTHMGR is not ready(startup phase=%d). waiting..",
             startupPhase);
    osapiSleep(1);
  }
  authmgrIssueCmd (authmgrIntfStartup,  NULL, &startupPhase);
}

/*********************************************************************
* @purpose  Process NIM startup callback
*
* @param    startupPhase @b{(input)) CREATE or ACTIVATE
*
* @returns   SUCCESS
* @returns   FAILURE
*
* @comments none
*
* @end
*********************************************************************/
RC_t authmgrIhProcessIntfStartup (NIM_STARTUP_PHASE_t startup_phase)
{
  RC_t rc;
  uint32 intIfNum = 0;
  PORTEVENT_MASK_t nimEventMask;
  uint32 startTime, endTime;

  AUTHMGR_EVENT_TRACE (AUTHMGR_TRACE_EVENTS, 0,
                       "%s:%d Received event %d from NIM  \n",
                       __FUNCTION__, __LINE__, startup_phase);

  startTime = osapiTimeMillisecondsGet ();
  switch (startup_phase)
  {
  case NIM_INTERFACE_CREATE_STARTUP:
    rc = nimFirstValidIntfNumber (&intIfNum);

    while (rc ==  SUCCESS)
    {
      if (authmgrIsValidIntf (intIfNum) ==  TRUE)
      {
        if (authmgrIntfCreate (intIfNum) !=  SUCCESS)
        {
           LOGF ( LOG_SEVERITY_ERROR,
                   "Error in creating interface %s.", authmgrIntfIfNameGet(intIfNum));
        }
      }
      rc = nimNextValidIntfNumber (intIfNum, &intIfNum);
    }


    /* Now ask NIM to send any future changes for these event types */
    memset (&nimEventMask, 0, sizeof (PORTEVENT_MASK_t));
    PORTEVENT_SETMASKBIT (nimEventMask,  CREATE);
    PORTEVENT_SETMASKBIT (nimEventMask,  DELETE);
    nimRegisterIntfEvents ( AUTHMGR_COMPONENT_ID, nimEventMask);
    break;

  case NIM_INTERFACE_ACTIVATE_STARTUP:

    rc = authmgrIntfActivateStartup ();

    /* Now ask NIM to send any future changes for these event types */
    memset (&nimEventMask, 0, sizeof (PORTEVENT_MASK_t));
    PORTEVENT_SETMASKBIT (nimEventMask,  CREATE);
    PORTEVENT_SETMASKBIT (nimEventMask,  DELETE);
    PORTEVENT_SETMASKBIT (nimEventMask,  ATTACH);
    PORTEVENT_SETMASKBIT (nimEventMask,  DETACH);
    PORTEVENT_SETMASKBIT (nimEventMask,  PORT_ENABLE);
    PORTEVENT_SETMASKBIT (nimEventMask,  PORT_DISABLE);
    PORTEVENT_SETMASKBIT (nimEventMask,  UP);
    PORTEVENT_SETMASKBIT (nimEventMask,  DOWN);
    PORTEVENT_SETMASKBIT (nimEventMask,  AUTHMGR_ACQUIRE);
    nimRegisterIntfEvents ( AUTHMGR_COMPONENT_ID, nimEventMask);

    (void) osapiTaskInitDone ( AUTHMGR_DB_TASK_SYNC);
    
    break;

  default:
    break;
  }

  endTime = osapiTimeMillisecondsGet ();

  nimStartupEventDone ( AUTHMGR_COMPONENT_ID);

  return  SUCCESS;
}

/*********************************************************************
* @purpose  Process Link state changes
*
* @param    intIfNum   @b{(input)) internal interface number
* @param    intIfEvent @b{(input)) interface event
* @param    correlator @b{(input)) correlator 
*
* @returns   SUCCESS
* @returns   FAILURE
*
* @comments none
*
* @end
*********************************************************************/
RC_t authmgrIhProcessIntfChange (uint32 intIfNum, uint32 intfEvent,
                                    NIM_CORRELATOR_t correlator)
{
  uint32 linkState, adminState;
  RC_t rc =  SUCCESS;
  NIM_EVENT_COMPLETE_INFO_t status;
  authmgrPortCfg_t *pCfg;
  uint32 lIntIfNum;
  authmgrLogicalPortInfo_t *logicalPortInfo;
   BOOL portEnabled =  FALSE;

  status.intIfNum = intIfNum;
  status.component =  AUTHMGR_COMPONENT_ID;
  status.event = intfEvent;
  status.correlator = correlator;

  if (!(AUTHMGR_IS_READY))
  {
     LOGF ( LOG_SEVERITY_INFO,
             "Received an interface change callback while AUTHMGR not ready to receive it.");
    AUTHMGR_EVENT_TRACE (AUTHMGR_TRACE_FAILURE, intIfNum,
                         "%s:%d Nim Event %d received for %s when authmgr is not ready \n",
                         __FUNCTION__, __LINE__, intfEvent, authmgrIntfIfNameGet(intIfNum));
    rc =  FAILURE;
    status.response.rc = rc;
    nimEventStatusCallback (status);
    return rc;
  }

  AUTHMGR_EVENT_TRACE (AUTHMGR_TRACE_EVENTS, intIfNum,
                       "%s:%d Nim Event %d received for %s \n", __FUNCTION__,
                       __LINE__, intfEvent, authmgrIntfIfNameGet(intIfNum));

  if (authmgrIsValidIntf (intIfNum) !=  TRUE)
  {
    rc =  SUCCESS;
    status.response.rc = rc;
    nimEventStatusCallback (status);
    return rc;
  }

  switch (intfEvent)
  {
    case  UP:
    case  PORT_ENABLE:
      if (authmgrIntfIsConfigurable (intIfNum, &pCfg) ==  TRUE)
      {
        portEnabled =  FALSE;
         authmgrCB->globalInfo->authmgrPortInfo[intIfNum].portEnabled =  FALSE;
        if (nimGetIntfLinkState (intIfNum, &linkState) ==  SUCCESS)
        {
          if ((nimGetIntfAdminState (intIfNum, &adminState) ==  SUCCESS) &&
              (adminState ==  ENABLE) &&
              (nimGetIntfLinkState (intIfNum, &linkState) ==  SUCCESS) &&
              (linkState ==  UP))
          {
            portEnabled =  TRUE;
          }
        }

        if ( DOT1X_PAE_PORT_AUTH_CAPABLE != pCfg->paeCapabilities)
        {
          authmgrCB->globalInfo->authmgrPortInfo[intIfNum].portEnabled = portEnabled;
          if ( TRUE == authmgrCB->globalInfo->authmgrPortInfo[intIfNum].portEnabled)
          {
            authmgrPhysicalPortStatusOpenSet(intIfNum);
          }
          else
          {
            authmgrPhysicalPortStatusBlockSet(intIfNum);
          }
        }
        else
        {
          if (authmgrCB->globalInfo->authmgrPortInfo[intIfNum].paeCapabilities ==
               DOT1X_PAE_PORT_AUTH_CAPABLE)
          {
            if (( TRUE == portEnabled) &&
                (portEnabled != authmgrCB->globalInfo->authmgrPortInfo[intIfNum].portEnabled))
            {
              authmgrCB->globalInfo->authmgrPortInfo[intIfNum].portEnabled =  TRUE;

              /* Cleanup any existing clients on the port before reset */
              authmgrPortInfoCleanup (intIfNum);
              (void) authmgrPortReset (intIfNum);
              (void) authmgrCtlApplyPortConfigData (intIfNum);
            }
          }
        }
      }
      break;

    case  DOWN:
    case  PORT_DISABLE:
      if ( TRUE == authmgrIntfIsConfigurable (intIfNum, &pCfg))
      {
        if ( DOT1X_PAE_PORT_AUTH_CAPABLE != pCfg->paeCapabilities)
        {
          authmgrPhysicalPortStatusBlockSet(intIfNum);
        }
        else 
        {
          if (authmgrCB->globalInfo->authmgrPortInfo[intIfNum].portEnabled ==
               TRUE)
          {
            authmgrCB->globalInfo->authmgrPortInfo[intIfNum].portEnabled =  FALSE;

            lIntIfNum = AUTHMGR_LOGICAL_PORT_ITERATE;
            while ((logicalPortInfo =
                  authmgrLogicalPortInfoGetNextNode (intIfNum,
                    &lIntIfNum)) !=  NULLPTR)
            {
              if (0 == logicalPortInfo->key.keyNum)
              {
                continue;
              }
            }

            if (authmgrCB->globalInfo->authmgrPortInfo[intIfNum].portControlMode ==
                 AUTHMGR_PORT_AUTO)
            {
              authmgrPortInfoCleanup (intIfNum);
            }
          }
          authmgrPhysicalPortStatusBlockSet(intIfNum);
        }
      }
      break;

    case  CREATE:
      rc = authmgrIntfCreate (intIfNum);
      break;

    case  ATTACH:
      if (authmgrIntfIsConfigurable (intIfNum, &pCfg) ==  TRUE)
      {
        if ( DOT1X_PAE_PORT_AUTH_CAPABLE != pCfg->paeCapabilities)
        {
          /*authmgrPhysicalPortStatusOpenSet(intIfNum); */
        }
        else
        {
          (void) authmgrPortReset (intIfNum);
          /*rc = authmgrCtlApplyPortConfigData(intIfNum); */
        }
      }
      else
      {
        rc =  FAILURE;
      }
      break;

    case  DETACH:
      rc = authmgrIntfDetach (intIfNum);
      break;

    case  DELETE:
      rc = authmgrIntfDelete (intIfNum);
      break;

    default:
      break;
  }

  status.response.rc = rc;
  nimEventStatusCallback (status);

  return rc;
}

/*********************************************************************
* @purpose  bring up the individual interface
*
* @param    intIfNum -- interface number 
* @param    warmRestart  BOOL true if restart type is warm
*
* @returns   SUCCESS
* @returns   FAILURE
*
* @comments Handle the events ATTACH, UP, Acquire etc
*
* @end
*********************************************************************/
RC_t authmgrIntfStart (uint32 intIfNum,  BOOL warmRestart)
{
  authmgrPortCfg_t *pCfg;
  RC_t rc =  FAILURE;
   uint32 portLinkState;
   uint32 adminState, count = 0;
   BOOL restore =  FALSE;
   BOOL needCleanup =  TRUE, skipFlag =  FALSE;

  do
  {
    if (authmgrIntfIsConfigurable (intIfNum, &pCfg) !=  TRUE)
    {
      return rc;
    }

    if ( DOT1X_PAE_PORT_AUTH_CAPABLE != pCfg->paeCapabilities)
    {
      authmgrPhysicalPortAccessSet(intIfNum);
      return  SUCCESS;
    }

    /* Link state */
    rc = nimGetIntfLinkState (intIfNum, &portLinkState);
    if (rc !=  SUCCESS)
    {
      break;
    }
    rc = nimGetIntfAdminState (intIfNum, &adminState);
    if (rc !=  SUCCESS)
    {
      break;
    }

    if (( TRUE != warmRestart) ||
        (0 == count))
    {
      (void) authmgrPortReset (intIfNum);
      if (authmgrCB->globalInfo->authmgrPortInfo[intIfNum].paeCapabilities ==
          DOT1X_PAE_PORT_AUTH_CAPABLE)
      {
        rc = authmgrCtlApplyPortConfigData (intIfNum);
      }
      else
      {
        rc =  SUCCESS;
      }
    }


    if ((portLinkState ==  UP) &&
        ( ENABLE == adminState))
    {
      if ((warmRestart ==  TRUE) && (restore) && (skipFlag ==  TRUE) &&
          (authmgrCB->globalInfo->authmgrPortInfo[intIfNum].portControlMode ==
           AUTHMGR_PORT_AUTO))
      {
        count = 0;
        if (0 != count)
        {
          needCleanup =  FALSE;
        }
      }
    }                         /* end if link state is up */
  }

  while ( FALSE);

  if ((0 == authmgrCB->globalInfo->authmgrPortInfo[intIfNum].authCount) &&
      (portLinkState ==  DOWN))  
  {
    authmgrPhysicalPortStatusBlockSet(intIfNum);
  }
  return rc;
}

/*********************************************************************
* @purpose  Process the activate startup callback from NIM
*
* @param -- none
*
* @returns   SUCCESS
* @returns   FAILURE
*
* @comments none
*
* @end
*********************************************************************/
RC_t authmgrIntfActivateStartup ()
{
  RC_t rc;
  uint32 intIfNum = 0;

  rc = nimFirstValidIntfNumber (&intIfNum);

  while (rc ==  SUCCESS)
  {
    if (authmgrIsValidIntf (intIfNum) ==  TRUE)
    {
      authmgrIntfStart (intIfNum, FALSE);
    }

    rc = nimNextValidIntfNumber (intIfNum, &intIfNum);
  }

  return  SUCCESS;
}

/*********************************************************************
* @purpose  Make sure this is a valid interface for authmgr
*
* @param    intIfNum   @b{(input)) internal interface number
*
* @returns   SUCCESS
* @returns   FAILURE
*
* @comments none
*
* @end
*********************************************************************/
RC_t authmgrIhIntfValidate (uint32 intIfNum)
{
  /* Check for valid interface number */
  if (nimCheckIfNumber (intIfNum) !=  SUCCESS)
  {
    return  FAILURE;
  }

  /* Check for physical interface interface */
  if (authmgrIsValidIntf (intIfNum) !=  TRUE)
  {
    return  FAILURE;
  }

  return  SUCCESS;
}

/*********************************************************************
* @purpose  Obtain a pointer to the specified interface configuration data
*           for this interface
*
* @param    intIfNum @b{(input)} Internal Interface Number
* @param    **pCfg   @b{(output)}  Ptr  to authmgr port config structure
*                           or  NULL if not needed
*
* @returns   TRUE
* @returns   FALSE
*
*
* @comments Facilitates pre-configuration, as it checks if the NIM
*           interface exists and whether the component is in a state to
*           be configured (regardless of whether the component is enabled
*           or not).
*
* @comments The caller can set the pCfg parm to  NULL if it does not
*           want the value output from this function.
*
* @end
*********************************************************************/
 BOOL authmgrIntfIsConfigurable (uint32 intIfNum, authmgrPortCfg_t ** pCfg)
{
  nimConfigID_t configId;

  if (!(AUTHMGR_IS_READY))
  {
    return  FALSE;
  }

  /* Check boundary conditions */
  if (intIfNum <= 0 || intIfNum >=  AUTHMGR_INTF_MAX_COUNT)
  {
    return  FALSE;
  }

  /* verify that the configId in the config data table entry matches the
     configId that NIM maps to
     ** the intIfNum we are considering
   */
  if (nimConfigIdGet (intIfNum, &configId) ==  SUCCESS)
  {
    if (NIM_CONFIG_ID_IS_EQUAL
        (&configId,
         &(authmgrCB->globalInfo->authmgrCfg->authmgrPortCfg[intIfNum].
           configId)) ==  TRUE)
    {
      *pCfg = &authmgrCB->globalInfo->authmgrCfg->authmgrPortCfg[intIfNum];
      return  TRUE;
    }
  }

  /* if we get here, either we have a table management error between authmgrCfg
     and authmgrMapTbl or
     ** there is synchronization issue between NIM and components w.r.t. interface
     **creation/deletion
   */
   LOGF ( LOG_SEVERITY_INFO,
           "Error accessing AUTHMGR config data for interface %s in AUTHMGR interface configuration.\n",
           authmgrIntfIfNameGet(intIfNum));
  return  FALSE;
}

/*********************************************************************
* @purpose  Obtain a pointer to the first free interface config struct
*
* @param    intIfNum @b{(input)} Internal Interface Number
* @param    **pCfg   @b{(output)}  Ptr  to authmgr port config structure
*                           or  NULL if not needed
*
* @returns   TRUE
* @returns   FALSE
*
*
* @comments Facilitates pre-configuration, as it checks if the NIM
*           interface exists and whether the component is in a state to
*           be configured (regardless of whether the component is enabled
*           or not).
*
* @end
*********************************************************************/
 BOOL authmgrIntfConfigEntryGet (uint32 intIfNum, authmgrPortCfg_t ** pCfg)
{
  uint32 i;
  nimConfigID_t configId;
  nimConfigID_t configIdNull;
  RC_t rc;

  memset (&configIdNull, 0, sizeof (nimConfigID_t));

  if (!(AUTHMGR_IS_READY))
  {
    return  FALSE;
  }

  if ((rc = nimConfigIdGet (intIfNum, &configId)) ==  SUCCESS)
  {
    for (i = 1; i <  AUTHMGR_INTF_MAX_COUNT; i++)
    {
      if (NIM_CONFIG_ID_IS_EQUAL
          (&authmgrCB->globalInfo->authmgrCfg->authmgrPortCfg[i].configId,
           &configIdNull))
      {
        authmgrCB->globalInfo->authmgrMapTbl[intIfNum] = i;
        *pCfg = &authmgrCB->globalInfo->authmgrCfg->authmgrPortCfg[i];
        return  TRUE;
      }
    }
  }

  return  FALSE;
}

/*********************************************************************
*
* @purpose  To process the Callback for  CREATE
*
* @param    uint32  intIfNum  internal interface number
*
* @returns   SUCCESS
* @returns   FAILURE
*
* @comments none
*
* @end
*
*********************************************************************/
RC_t authmgrIntfCreate (uint32 intIfNum)
{
  nimConfigID_t configId;
  authmgrPortCfg_t *pCfg;

  if (authmgrIsValidIntf (intIfNum) !=  TRUE)
  {
    return  FAILURE;
  }

  if (nimConfigIdGet (intIfNum, &configId) !=  SUCCESS)
  {
    return  FAILURE;
  }

  pCfg = &authmgrCB->globalInfo->authmgrCfg->authmgrPortCfg[intIfNum];

  if (!(NIM_CONFIG_ID_IS_EQUAL (&configId, &pCfg->configId)))
  {
    NIM_CONFIG_ID_COPY (&pCfg->configId, &configId);
    /* If an interface configuration entry is not already assigned to the
       interface, assign one */
    /* Update the configuration structure with the config id */
    authmgrBuildDefaultIntfConfigData (&configId, pCfg);
  }

  /* Initialize the port info and clear the stats for this interface */
  authmgrCB->globalInfo->authmgrPortInfo[intIfNum].nimStatus =
     AUTHMGR_PORT_STATUS_NA;
  (void) authmgrCtlPortStatsClear (intIfNum);

  return  SUCCESS;
}

/*********************************************************************
*
* @purpose  To process the Callback for  DETACH
*
* @param    uint32  intIfNum  internal interface number
*
* @returns   SUCCESS
* @returns   FAILURE
*
* @comments none
*
* @end
*
*********************************************************************/
RC_t authmgrIntfDetach (uint32 intIfNum)
{
  authmgrPortCfg_t *pCfg;

  if (authmgrIntfIsConfigurable (intIfNum, &pCfg) ==  TRUE)
  {
    authmgrPortInfoCleanup (intIfNum);
    if (authmgrCB->globalInfo->authmgrPortInfo[intIfNum].paeCapabilities ==
         DOT1X_PAE_PORT_AUTH_CAPABLE)
    {
      (void) authmgrCtlApplyPortConfigData (intIfNum);
    }
  }

  return  SUCCESS;
}

/*********************************************************************
*
* @purpose  To process the Callback for  DELETE
*
* @param    uint32  intIfNum  internal interface number
*
* @returns   SUCCESS
* @returns   FAILURE
*
* @comments none
*
* @end
*
*********************************************************************/
RC_t authmgrIntfDelete (uint32 intIfNum)
{
  authmgrPortCfg_t *pCfg;

  if (authmgrIntfIsConfigurable (intIfNum, &pCfg) ==  TRUE)
  {
    authmgrPortInfoCleanup (intIfNum);

    memset ((void *) &pCfg->configId, 0, sizeof (nimConfigID_t));
    memset ((void *) &authmgrCB->globalInfo->authmgrMapTbl[intIfNum], 0,
            sizeof (uint32));
    memset ((void *) &authmgrCB->globalInfo->authmgrPortInfo[intIfNum], 0,
            sizeof (authmgrPortInfo_t));
    memset ((void *) &authmgrCB->globalInfo->authmgrPortStats[intIfNum], 0,
            sizeof (authmgrPortStats_t));
  }

  return  SUCCESS;
}


/*********************************************************************
* @purpose  Set the Violation Callback in the driver
*
* @param    intIfNum   @b{(input)) internal interface number
* @param    flag       @b{(input)) True to set the value
*
* @returns   SUCCESS
* @returns   FAILURE
* @returns   ERROR
*
* @comments none
*
* @end
*********************************************************************/
RC_t authmgrIhPhyPortViolationCallbackSet (uint32 intIfNum,
                                               AUTHMGR_PORT_VIOLATION_CALLBACK_t
                                              flag)
{
  RC_t rc =  SUCCESS;

  if ( AUTHMGR_PORT_VIOLATION_CALLBACK_ENABLE == flag)
  {
    authmgrCB->globalInfo->authmgrPortInfo[intIfNum].unLearnMacPolicy =  TRUE;
  }
  else
  {
    authmgrCB->globalInfo->authmgrPortInfo[intIfNum].unLearnMacPolicy =  FALSE;
  }

  return rc;
}

